import { EmailAddress, FolderType, IFolder, MailEngine } from '@coremail/mail_base';

type FolderId = string;

export type FolderItem = {
  name: string;
  children: FolderItem[];
  id: FolderId;
  type: FolderType,
  isHighlight?: boolean;
}

export type MailId = [string, string];

export type MailItemAbstract = {
  id: MailId;
  subject: string;
  from: EmailAddress;
  digest: string;
  time: Date;
  isHighlight?: boolean;
}

type MailItem = MailItemAbstract & {
  html: string;
  plain: string;
  attachments: string[];
}

export type ViewModel = {
  folderList: FolderItem[];
  currentFolder?: FolderItem;
  mailList: MailItemAbstract[];
  currentMail?: MailItem;
}

export class MailViewModel {
  _vm: ViewModel;
  _engine?: MailEngine;

  constructor() {
    this._vm = {
      folderList: [],
      mailList: []
    }
  }

  getData(): ViewModel {
    return this._vm;
  }

  async setMainEngine(engine: MailEngine) {
    this._engine = engine;
    const root = await engine.getRootFolder();
    const folderItem = await this.folder2FolderItem(root);
    this._vm = {
      folderList: folderItem.children,
      mailList: [],
    }
    if (folderItem.children.length > 0) {
      const f = folderItem.children.find(item => item.type == FolderType.inbox);
      if (f) {
        await this.setCurrentFolder(f.id);
      }
    }
  }

  async folder2FolderItem(folder: IFolder): Promise<FolderItem> {
    const subFolders = await folder.getSubFolders();

    return {
      name: folder.getName(),
      id: folder.getFullName(),
      type: folder.getType(),
      children: await Promise.all(subFolders.map(f => this.folder2FolderItem(f)))
    }
  }

  findFolder(fid: FolderId): FolderItem | undefined {
    let fi: FolderItem;
    this._iterFolder(this._vm.folderList, (f: FolderItem) => {
      if (f.id == fid) {
        fi = f;
      }
    });
    return fi;
  }

  _iterFolder(folders: FolderItem[], handler: (f: FolderItem) => void) {
    folders.forEach(handler);
    for (const folder of folders) {
      this._iterFolder(folder.children, handler);
    }
  }

  async setCurrentFolder(fid: FolderId | undefined) {
    if (!fid) {
      this._vm = { ...this._vm, currentFolder: undefined }
    } else if (fid == this._vm.currentFolder?.id) {
      return;
    } else {
      const currentFolder = this.findFolder(fid);
      if (!currentFolder) {
        return;
      }
      const folder = await this._engine!.getFolder(fid);
      const mailList = await folder.getMailList(10);
      const mailItemList = await Promise.all(mailList.map(async m => {
        return {
          id: [m.getFolder().getFullName(), m.getId()] as MailId,
          subject: await m.getSubject(),
          from: await m.getFrom(),
          digest: '',
          time: await m.getDate(),
        }
      }))
      const folderList = [...this._vm.folderList];
      this._iterFolder(folderList, f => {
        f.isHighlight = f.id == currentFolder.id
      });
      this._vm = { folderList, currentFolder, mailList: mailItemList };
      Promise.all(mailList.map(async mail => {
        const digest = await mail.getDigest(30);
        for (const mailItem of this._vm.mailList) {
          if (mailItem.id[0] == mail.getFolder().getFullName() && mailItem.id[1] == mail.getId()) {
            mailItem.digest = digest;
            this._vm.mailList = [...this._vm.mailList];
            break;
          }
        }
      }))
      if (mailList.length) {
        const mail = mailList[0];
        this._vm = {
          ...this._vm,
          currentMail: {
            ...mailItemList[0],
            html: await mail.getHtml(),
            plain: await mail.getPlain(),
            attachments: (await mail.getAttachments()).map(a => a.getFileName())
          }
        };
      }
    }
  }

  async setCurrentMail(mid: MailId) {
    const folder = await this._engine!.getFolder(mid[0]);
    const mail = await folder.getMail(mid[1]);
    this._vm = {
      ...this._vm,
      currentMail: {
        id: [mail.getFolder().getFullName(), mail.getId()] as MailId,
        subject: await mail.getSubject(),
        from: await mail.getFrom(),
        digest: await mail.getDigest(30),
        time: await mail.getDate(),
        html: await mail.getHtml(),
        plain: await mail.getPlain(),
        attachments: (await mail.getAttachments()).map(a => a.getFileName())
      }
    }
  }
}
